import collections
import json
import os.path
from werkzeug.utils import secure_filename
from flask import Flask, request,jsonify
import sqlite3
from datetime import datetime
import uuid
import configparser
import random
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

from pathlib import Path

import asyncio
# from kalyke import ApnsClient, ApnsConfig, Payload, PayloadAlert
import threading
import shutil
import mqtt
import beautifulJson
# 配置
pem_file_path = "path/to/apns.pem"         # 推送证书路径
m_client = None


# apnClient = ApnsClient(
#     use_sandbox=True,
#     team_id="YOUR_TEAM_ID",
#     auth_key_id="AUTH_KEY_ID",
#     auth_key_filepath=pem_file_path,
# )

app = Flask(__name__)
db_path = "audio_server.db"


Inputs_FOLDER = ''
Outputs_FOLDER = ''
report_level = ''

if not os.path.exists(db_path):
    print(f"Database file audio_app.db does not exist.")
else:
    conn = sqlite3.connect(db_path, check_same_thread=False)
    conn.row_factory = sqlite3.Row

def errorResult(code,msg):
    return jsonify({
        "code": code,
        "msg":msg
    })

def successResult(data,msg=''):
    return jsonify({
        "code": 200,
        "msg":msg,
        "data": data
})



def curDate():
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

def saveAudioFile(file,folder,filepath):
    os.makedirs(folder, exist_ok=True)
    file.save(filepath)

processed_files = set()
def clearProcessFile(key):
    processed_files.remove(key)

# def push(token_hex,title):
#     payload_alert = PayloadAlert(title=f"{title}的报告已生成", body="")
#     payload = Payload(alert=payload_alert, badge=1, sound="default")
#     config = ApnsConfig(topic="com.example.App")
#     asyncio.run(
#         apnClient.send_message(
#             device_token=token_hex,
#             payload=payload,
#             apns_config=config,
#         )
#     )
#     payload = Payload(alert=f"{title}的报告已生成", sound="default", badge=1)
#     topic = '报告生成成功'
#     client = ApnsClient(pem_file_path, use_sandbox=False, use_alternative_port=False)
#     client.send_notification(token_hex, payload, topic)
#     Notification = collections.namedtuple('Notification', ['token', 'payload'])
#     notifications = [Notification(payload=payload, token=token_hex)]
#     client.send_notification_batch(notifications=notifications, topic=topic)
#     print("调用推送")

#登录接口
@app.route('/login',methods=['GET','POST'])
def login():
    username = request.args.get('username')
    if username is None or username == "":
        username = request.form.get('username')
    password = request.args.get("password")
    if password is None or password == "":
        password = request.form.get('password')
    if username is None or username == "":
        return  errorResult(500,'用户名不能为空')
    if password is None or password == "":
        return errorResult(500, '密码不能为空')
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM sys_user where username=? and password=?',(username,password ))
    row = cursor.fetchall()
    cursor.close()
    if len(row)==0:
        return errorResult(500, '账号或密码错误')
    info = dict(row[0])
    return successResult({
        "username":info['username'],
        "userId":info['user_id'],
        "nickname":info['nickname']
    })

#苹果 token注册
@app.route('/push/register',methods=['POST'])
def registerPush():
    token = request.form.get['token']
    userId = request.form.get['userId']
    if token is None or token == "":
        return errorResult(500, 'token为空')
    if userId is None or userId == "":
        return errorResult(500, 'userId为空')
    cursor = conn.cursor()
    cursor.execute('update sys_user set push_token = ?  WHERE user_id = ?', (token, userId))
    conn.commit()
    cursor.close()
    return successResult(None,"提交成功")

def findUserName(cursor,user_id):
    row = cursor.execute('select * from sys_user where user_id=?',(user_id,)).fetchone()
    if row is None:
        return user_id
    return  row['username']

def newFileName(filename,folder,ext):
    counter = 1
    filename = filename.replace(" ", "").replace(":", "")
    new_filename = filename
    while os.path.exists(os.path.join(folder, new_filename+ext)):
        new_filename = f"{filename}({counter})"
        counter += 1
    return new_filename

def handleFile(cursor,file_info,files,user_id,last_id):
    #查询下用户名称
    userFolder = findUserName(cursor,user_id)
    # 保存下子表
    for index, item in enumerate(file_info):
        filename, file_extension = os.path.splitext(files[index].filename)

        folder = 'VEX'
        if 'mp3' in file_extension.lower():
            folder = 'MP3'
        parentFolder = f'{Inputs_FOLDER}/{userFolder}/{last_id}/{folder}'
        filename = newFileName(filename,parentFolder,file_extension) + file_extension
        save_path = f'/{userFolder}/{last_id}/{folder}/{filename}'
        file_path = os.path.join(parentFolder, filename)
        cursor.execute(
            'INSERT INTO audio_file (record_id, filepath, filename,create_time,user_id,type,origin,duration) VALUES (?,?,?,?,?,?,?,?)',
            (last_id, save_path, item['filename'], curDate(), user_id, item['type'], item['origin'], item['duration']))
        saveAudioFile(files[index], parentFolder, file_path)

        # 创建同名文件夹，对应的output
        outputFolder = f'{Outputs_FOLDER}/{userFolder}/{last_id}/{folder}'
        os.makedirs(outputFolder, exist_ok=True)
    conn.commit()
    #创建空文件
    with open(f"{Path(parentFolder).parent}/ready.txt","w") as file:
        pass

    #TODO 后续删除
    # outputFolder = parentFolder.replace("Inputs","Outputs")
    # os.makedirs(Path(outputFolder).parent, exist_ok=True)
    # timer = threading.Timer(10, my_task,args=(Path(outputFolder).parent,))
    # timer.start()

#录音上传接口
@app.route('/audioRecord',methods=['POST'])
def audioRecordPost():
    if 'file' not in request.files:
        return errorResult(500, '录音文件不能为空')
    files = request.files.getlist('file')
    title = request.form.get("title")
    remark = request.form.get("remark")
    user_id = request.headers.get("userid")

    file_info = json.loads(request.form.get("file_info"))
    # 保存下主表
    cursor = conn.cursor()
    cursor.execute('INSERT INTO audio_record (title, remark, create_time,user_id) VALUES (?, ?, ?,?)', (title, remark,curDate(),user_id))
    last_id = cursor.lastrowid
    handleFile(cursor,file_info,files, user_id, last_id)
    conn.commit()
    cursor.close()
    return successResult(last_id,"上传成功")

#修改接口
@app.route('/audioRecord/edit',methods=['POST'])
def audioRecordUpdate():
    title = request.form.get("title")
    record_id = request.form.get("record_id")
    remark = request.form.get("remark")
    user_id = request.headers.get("userid")
    cursor = conn.cursor()
    cursor.execute('update audio_record set title = ?,remark=?,update_time=?,update_by=?  WHERE record_id = ?', (title,remark,curDate(),user_id,record_id))
    conn.commit()
    cursor.close()
    return successResult(record_id,"提交成功")

#录音上传接口
@app.route('/audioFile',methods=['POST'])
def audioUpload():
    if 'file' not in request.files:
        return errorResult(500, '录音文件不能为空')
    files = request.files.getlist('file')
    user_id = request.headers.get("userid")
    record_id = request.form.get("record_id")
    file_info = json.loads(request.form.get("file_info"))
    cursor = conn.cursor()
    handleFile(cursor,file_info,files, user_id, record_id)
    conn.commit()
    cursor.close()
    return successResult(record_id,"上传成功")

#记录详情
@app.route('/audioRecord/<record_id>',methods=['GET'])
def audioDetail(record_id):
    if record_id is None or record_id == "":
        return errorResult(500, '记录id不能为空')
    cursor = conn.cursor()
    row = cursor.execute('select * from audio_record where record_id = ?', (record_id,)).fetchone()
    if row is None:
        return errorResult(500, '记录id不存在')
    audioRecord = dict(row)
    file_info = cursor.execute('select * from audio_file where record_id = ? and del_flag=0 order by create_time asc', (record_id,)).fetchall()
    audioRecord['file_info'] = [dict(row) for row in file_info]
    cursor.close()
    return successResult(audioRecord)

#删除记录文件
@app.route('/audioRecord/<record_id>',methods=['DELETE'])
def audioRecordDel(record_id):
    if record_id is None or record_id == "":
        return errorResult(500, '记录id不能为空')
    cursor = conn.cursor()
    cursor.execute('update audio_record set del_flag = ?  WHERE record_id = ?', (2,record_id))
    conn.commit()
    cursor.close()
    return successResult(record_id,"删除成功")

#删除语音文件
@app.route('/audioFile/<file_id>',methods=['DELETE'])
def audioFileDel(file_id):
    if file_id is None or file_id == "":
        return errorResult(500, '文件id不能为空')
    cursor = conn.cursor()
    cursor.execute('update audio_file set del_flag = ?  WHERE id = ?', (2,file_id))
    conn.commit()
    cursor.close()
    return successResult(file_id,"删除成功")

#获取记录列表
@app.route('/audioRecord/list',methods=['GET'])
def audioRecordList():
    pageNum = request.args.get('pageNum',default=1) or 1
    pageSize = request.args.get('pageSize',default=20) or 20
    user_id = request.headers.get("userid")
    title = request.args.get('title', default='')
    cursor = conn.cursor()
    if not title:
        sql = ('select t1.* ,t3.nickname,count(id) as file_num ,SUM(distinct CASE WHEN t2.pdf IS NULL THEN 0 ELSE 1 END) AS pdf_num '
               'from audio_record t1 LEFT JOIN audio_file t2 on t1.record_id = t2.record_id '
               'LEFT JOIN sys_user t3 on t1.user_id=t3.user_id where t1.user_id = ? and t1.del_flag=0 and t2.del_flag=0 GROUP BY t1.record_id order by t1.create_time desc  limit ?,?')
        cursor.execute(sql, (user_id, (int(pageNum)-1)*int(pageSize),pageSize))
    else:
        sql = (
            'select t1.* ,t3.nickname,count(id) as file_num ,SUM(distinct CASE WHEN t2.pdf IS NULL THEN 0 ELSE 1 END) AS pdf_num '
            'from audio_record t1 LEFT JOIN audio_file t2 on t1.record_id = t2.record_id '
            'LEFT JOIN sys_user t3 on t1.user_id=t3.user_id where t1.user_id = ? and t1.del_flag=0 and t2.del_flag=0 and t1.title like ? GROUP BY t1.record_id order by t1.create_time desc  limit ?,?')
        cursor.execute(sql, (user_id,f'%{title}%', (int(pageNum)-1)*int(pageSize),pageSize))
    rows = cursor.fetchall()
    cursor.close()
    list = [dict(row) for row in rows]
    return successResult(list)

#获取文件列表
@app.route('/audio/clear',methods=['GET'])
def clearAudio():
    cursor = conn.cursor()
    cursor.execute('delete from audio_record')
    cursor.execute('delete from audio_file')
    conn.commit()
    cursor.close()

#获取文件列表
@app.route('/audioFile/list',methods=['GET'])
def audioFileList():
    pageNum = request.args.get('pageNum',default=1) or 1
    pageSize = request.args.get('pageSize',default=20) or 20
    title = request.args.get('title',default='')
    user_id = request.headers.get("userid")
    cursor = conn.cursor()
    if not title:
        cursor.execute('SELECT * FROM audio_file where user_id=? and del_flag=? order by create_time desc limit ?,?', (user_id,0, (int(pageNum)-1)*int(pageSize),pageSize))
    else:
        cursor.execute('SELECT * FROM audio_file where user_id=? and del_flag=? and filename like ? order by create_time desc   limit ?,?', (user_id,0,f'%{title}%', (int(pageNum)-1)*int(pageSize),pageSize))
    rows = cursor.fetchall()
    cursor.close()
    list = [dict(row) for row in rows]
    return successResult(list)

def sendMqtt(msg):
    mqtt.publish(m_client,msg)

def audioTypeReport(event):
    if report_level == "2":
        #上一级
        reportId = Path(event.src_path).parent.name
    else:
        #同一级
        reportId = Path(event.src_path).parent.parent.name
    fileName = Path(event.src_path).name
    folder = str(Path(event.src_path).parent).replace(Outputs_FOLDER, "")
    pdf_path = f'{folder}/{Path(event.src_path).name}'.replace("\\","/")
    cursor = conn.cursor()
    fileType = "1"
    if "VEX_" in fileName:
        fileType = "2"
    cursor.execute("update audio_file set pdf = ?,pdf_time=?  WHERE record_id like ? and type=?",
                   (pdf_path, curDate(), f"%{reportId}%",fileType))
    conn.commit()
    cursor.close()
    info = {"record_id": int(reportId), "pdf": pdf_path,"code":200}
    sendMqtt(json.dumps(info, default=str))
    timer = threading.Timer(1, clearProcessFile,args=(event.src_path,))
    timer.start()

def createPdf(event):
    try:
        with open(event.src_path, 'r', encoding='utf-8') as f:
            json_data = json.load(f)
            generator = beautifulJson.ReportGenerator(json_data)
            reportId = "MP3"
            if "MP3" not in event.src_path:
                reportId = "VEX"
            output_filename = os.path.join(Path(event.src_path).parent,reportId+'_'+str(uuid.uuid1())+'.pdf')
            generator.generate_pdf(output_filename)
            print(f"PDF报告已生成：{output_filename}")
    except Exception as e:
        print(f"生成PDF报告失败: {e}")
    processed_files.remove(event.src_path)

def handleFailed(event):
    if report_level == "2":
        #上一级
        reportId = Path(event.src_path).parent.name
    else:
        #同一级
        reportId = Path(event.src_path).parent.parent.name
    cursor = conn.cursor()
    if "failed_MP3" in event.src_path:
        cursor.execute("update audio_file set failed = ?,pdf_time=?  WHERE record_id like ? and type=?",
                       (1, curDate(), f"%{reportId}%", 1))
    elif "failed_VEX" in event.src_path:
        cursor.execute("update audio_file set failed = ?,pdf_time=?  WHERE record_id like ? and type=?",
                       (1, curDate(), f"%{reportId}%", 2))
    elif "failed_MP3_and_VEX" in event.src_path:
        cursor.execute("update audio_file set failed = ?,pdf_time=?  WHERE record_id like ?",
                       (1, curDate(), f"%{reportId}%"))
    conn.commit()
    info = {"record_id": int(reportId), "code": 500}
    sendMqtt(json.dumps(info, default=str))
    cursor.close()
    processed_files.remove(event.src_path)

def handleReport(event):
    # 监听文件创建事件
    if event.is_directory:
        return
    if 'failed_' in event.src_path.lower():
        if event.src_path in processed_files:
            return
        processed_files.add(event.src_path)
        print(f'有失败 {event.src_path} ')
        timer = threading.Timer(1, handleFailed, args=(event,))
        timer.start()
    elif event.src_path.lower().endswith(".json"):
        if event.src_path in processed_files:
            return
        processed_files.add(event.src_path)
        print(f'文件 {event.src_path} 已创建')
        timer = threading.Timer(1, createPdf, args=(event,))
        timer.start()
    else:
        if not event.src_path.lower().endswith(".pdf"):
            return
        if not Path(event.src_path).exists():
            return
        if event.src_path in processed_files:
            return
        processed_files.add(event.src_path)
        print(f'文件 {event.src_path} 已创建')
        # oneReport(event)
        audioTypeReport(event)

class AudioPdfFileHandler(FileSystemEventHandler):

    def on_modified(self,event):
        handleReport(event)
    #监听文件的创建时间
    def on_created(self, event):
        handleReport(event)

# pyinstaller --onefile server.py
if __name__ == '__main__':
    m_client = mqtt.connect_mqtt(str(uuid.uuid1()))
    m_client.loop_start()

    config = configparser.ConfigParser()
    config.read("config.ini", encoding="utf-8")
    Inputs_FOLDER = config.get("folder", "Inputs_FOLDER")
    Outputs_FOLDER = config.get("folder", "Outputs_FOLDER")
    report_level = config.get("folder", "report_level")
    PORT = config.get("server", "port")

    # PDF_FOLDER = config.get("folder", "PDF_FOLDER")

    #创建下文件目录
    os.makedirs(Inputs_FOLDER, exist_ok=True)
    os.makedirs(Outputs_FOLDER, exist_ok=True)
    event_handler = AudioPdfFileHandler()  # 创建事件处理器
    observer = Observer()
    print("----file observer------")
    observer.schedule(event_handler, path=Outputs_FOLDER, recursive=True)  # 设置观察目录

    # 启动观察
    observer.start()
    app.run(debug=False,host="0.0.0.0",port=PORT)